from odoo import models, _, api, fields
from odoo.exceptions import UserError, ValidationError
import requests


class ResearchAndDevelopmentOrder(models.Model):
    _inherit = 's.research.and.development.order'

    def post_mes_async(self, production_ids=None):
        token = self.env['s.mes.login'].sudo().get_mes_token()
        for sale in self:
            if not sale.mrp_production_count: continue
            dtl = self.get_dtl(sale, production_ids)
            data = {
                "docType": "API_SO",
                "updateType": "UPDATE",
                "data": [dtl]
            }
            headers = {
                'Content-Type': 'application/json',
                'accessToken': token,
            }
            try:
                url = self.env['s.api.config_parameter'].search([('s_key', '=', 'mes.updateImsData')]).s_value
                record_vals = {
                    'api_route': url,
                    'api_params': data,
                    'state': '',
                    'result_json': '',
                    'doc_type': 'API_SO'
                }
                res = requests.post(url, json=data, headers=headers)
                result = res.json()
                if result.get('resultCode') != '0000':
                    record_vals['state'] = 'fail'
                    record_vals['result_json'] = str(result.get('resultMsg'))
                else:
                    record_vals['state'] = 'success'
                    mrp_production_ids = sale.line_ids.mapped('mrp_production_ids')
                    for production in mrp_production_ids:
                        self.s_set_production_meslock(production)
            except Exception as e:
                record_vals['state'] = 'fail'
                record_vals['result_json'] = e
            self.env['api.records'].create(record_vals)

    def s_set_production_meslock(self, production):
        children_ids = [production]  # 全部内层MO
        visited = [production]
        production = visited[0]
        while visited and visited.pop(0):
            children_ids.extend([p for p in production._get_children()])
            visited.extend([p for p in production._get_children()])
            if visited:
                production = visited[0]
        for c in children_ids:
            c.s_mes_lock = True
        return children_ids

    def get_dtl(self, sale, production_ids=None):
        orderline = sale.line_ids.filtered(lambda x: x.product_id.detailed_type == 'product')
        if len(orderline) != 1:
            raise UserError(_('销售订单[%s]存在多条产品，请检查！' % sale.name))
        plans = self.get_plans(orderline, production_ids)
        cast_qty = sum([rec.get('p_qty') for rec in plans])
        dtl = {
            "so_code": sale.name,  # 销售订单编码
            "org_code": '0001',  # 组织编号
            "org_name": "WTT",  # 组织名称
            "prod_code": orderline.product_id.product_tmpl_id.default_code,  # 产品编码
            "prod_name": orderline.product_id.product_tmpl_id.name,  # 产品名称
            "sku_code": orderline.product_id.product_tmpl_id.versions,  # SKU编码
            "cust_code": 'wtt001',  # 客户编码
            "cust_name": 'WTT',  # 客户名称
            "cust_no": orderline.product_id.product_tmpl_id.default_code,  # 客户型号
            "cust_version": orderline.product_id.product_tmpl_id.versions,  # 客户版本
            "cust_po": '',  # 客户PO
            "cust_pn": '',
            'cust_end_po': '',
            'cust_end_pn': '',
            "cust_po_seq": 1,  # 客户PO行号
            "line_number": 1,  # 行号
            "order_quantity": orderline.product_qty,  # 订单数量（PCS）
            # "remark": None,  # 备注
            # "bad_board_per": 0,  # 叉板比例
            "sales_type": 2,  # 销售类型 1内销，2外销
            "alIn_qty": 0,  # 已审核入库数量（PCS）
            # "po_id": None,  # 采购ID
            # "po_code": None,  # 采购单号
            "created_by": sale.create_uid.name,  # 创建人
            "created_date": sale.create_date.strftime("%Y-%m-%d %H:%M:%S"),  # 创建时间
            "updated_by": sale.write_uid.name,  # 修改人
            "updated_date": sale.write_date.strftime("%Y-%m-%d %H:%M:%S"),  # 修改时间
            "plan_code": sale.name,  # 计划单编码
            "order_stat": 1,  # 订单状态  1已下达，2未下达
            "version": orderline.product_id.product_tmpl_id.versions or '0',  # 版本
            # "contract_no": None,  # 合同号
            "order_qty": 0,  # 交货数量
            "order_type": 2,  # 订单类型  1量产，2样品，3测试，4客诉，5返工，6补投
            "plan_qty": orderline.product_qty,  # 计划单数量
            "cast_qty": orderline.planned_qty,  # 投料数量
            "reach_date": sale.planned_receiving_date.strftime("%Y-%m-%d %H:%M:%S"),  # 交货日期
            "unit": "PNL",  # 单位  1PNL，2SET
            "plan_start_date": sale.date.strftime("%Y-%m-%d %H:%M:%S"),  # 计划开始日期
            "pack_qty": 0,  # 包装数量
            "to_qty": 0,  # 入库数量
            "out_qty": 0,  # 出货数量
            "is_valid": "Y",  # 启用状态 N未启用，Y已启用
            "plan_end_date": sale.planned_receiving_date.strftime("%Y-%m-%d %H:%M:%S"),  # 计划结束日期
            "is_complete": "N",  # 完成状态  Y是，N否
            "out_type": 1,  # 出货类型 1出货数量>订单数量，2出货数量<=订单数量
            # "complete_by": None,  # 完工人
            # "complete_date": None,  # 完成时间
            "plans": plans
        }
        return dtl

    def get_plans(self, orderline, production_ids=None):
        if not orderline.mrp_production_ids:
            raise UserError(_('生产单[%s]未设置生产计划' % orderline.order_id.name))
        plans = []
        mes_flag_time = self.env['s.api.config_parameter'].search([('s_key', '=', 'mes.flag_date')], limit=1).s_value
        if not mes_flag_time:
            raise ValidationError('未配置时间节点')

        mes_flag_time = self.env['s.api.config_parameter'].search([('s_key', '=', 'mes.flag_date')], limit=1).s_value
        if not mes_flag_time:
            raise ValidationError('未配置时间节点')
        if not production_ids:
            production_ids = orderline.s_mrp_production_ids.filtered(lambda x: x.state in ['draft', 'confirmed', 'progress'] and fields.Datetime.to_string(x.create_date) > mes_flag_time)
        for production in production_ids:
            plans.extend(self.get_plan_child(production, orderline))
        return plans

    def get_plan_child(self, production, orderline):
        def top_production(production):
            # 最顶层production
            ids = production._get_sources()
            parent_ids = [rec for rec in ids]
            if not parent_ids:
                origin = production
            else:
                production = parent_ids[0]
                while parent_ids:
                    production = parent_ids.pop()
                    ids = production._get_sources()
                    parent_ids = [rec for rec in ids]
                origin = production
            return origin
        t_production = top_production(production)
        plans = []
        try:
            production.param_line_ids.filtered(lambda x: x.attribute_id.name.lower() == '1')
        except Exception as e:
            raise UserError(_('产品[%s]存在行未设置属, 请检查！' % production.product_id.name))
        tg = production.param_line_ids.filtered(lambda x: x.attribute_id.name.lower() == 'Tg'.lower())
        if len(tg) != 1:
            raise UserError(_('产品[%s]未设置Tg' % production.product_id.name))
        level = production.param_line_ids.filtered(lambda x: x.attribute_id.name.lower() == '层数'.lower())
        if len(level) != 1:
            raise UserError(_('产品[%s]未设置层数' % production.product_id.name))
        try:
            production.uom_value_ids.filtered(lambda x: x.uom_id.name.lower() == '1'.lower())
        except Exception as e:
            raise UserError(_('产品[%s]设置单位不能为空' % production.product_id.name))
        s_size = self.get_unit_number(production.product_id)
        # return [pnl_width, pnl_length, set_width, set_length, pcs_width, pcs_length, panel_qty, set_qty, pcs_qty]
        pcs = production.uom_value_ids.filtered(lambda x: x.uom_id.name.lower() == 'PCS'.lower())
        if len(pcs) != 1 and not s_size[-1]:
            raise UserError(_('产品[%s]未设置PCS单位' % production.product_id.name))
        pcs_qty = pcs.uom_value if len(pcs) else s_size[-1]
        setx = production.uom_value_ids.filtered(lambda x: x.uom_id.name.lower() == 'SET'.lower())
        if len(setx) != 1 and not s_size[-2]:
            raise UserError(_('产品[%s]未设置SET单位' % production.product_id.name))
        set_qty = setx.uom_value if len(setx) else s_size[-2]
        pnl = production.uom_value_ids.filtered(lambda x: x.uom_id.name.lower() == 'PANEL'.lower())
        if len(pnl) != 1 and not s_size[-3]:
            raise UserError(_('产品[%s]未设置PANEL单位' % production.product_id.name))
        pnl_qty = pnl.uom_value if len(pnl) else s_size[-3]
        mo_list = self.get_mo_list(production, orderline, tg, level)
        mrp_production_ids = production._get_children()
        s_mrp_production_ids = [m for m in mrp_production_ids]
        while s_mrp_production_ids:
            p = s_mrp_production_ids.pop(0)
            childs = p._get_children()
            s_mrp_production_ids.extend([c for c in childs])
            mrp_production_ids |= childs
        # for p in s_mrp_production_ids:
        #     mrp_production_ids |= p._get_children()
        for child in mrp_production_ids:
            mo_list.extend(self.get_mo_list(child, orderline, tg, level, child=True))
        # s_value_pnl = t_production.s_value_pnl
        s_value_set = t_production.s_value_set
        plans.append({
            "wplan_code": '',  # 主计划编码
            "plan_code": production.name,  # 计划单编码
            # "erp_mo": None,  # ERP工单编码
            "mo_type": 3,  # 工单类型 1:量产订单，2:样品订单，3:测试订单，4:客诉订单，5:返工订单，6:补投订单
            "prod_code": production.product_id.default_code,  # 产品编码
            "p_qty": production.product_qty,  # 计划数量 PCS, 看销售订单单位
            "set_qty": t_production.panel_qty * s_value_set,  # set数量sum(mo_list)
            "pnl_qty": t_production.panel_qty,  # pnl数量sum(mo_list)
            "pcs_qty": t_production.product_qty,  # PCS数量
            "pb_date": production.create_date.strftime("%Y-%m-%d %H:%M:%S"),  # 计划开始时间
            "pe_date": orderline.order_id.planned_receiving_date.strftime("%Y-%m-%d %H:%M:%S"),  # 计划结束时间
            "bom_ver": production.bom_id.product_tmpl_id.versions,  # BOM版本
            "phase_code": "PCB",  # 工艺段代码
            "wplan_code_par": None,  # 父主计划代码
            "wplan_stat": 1,  # 主计划状态 0:未下达，1:已下达，2:部分下达
            "wplan_code_src": None,  # 分拆来源主计划代码
            "pt_code": None,  # 车间代码
            "is_outs": "N",  # 外协 Y/N
            "sup_code": None,  # 供应商编码
            "delivery_date": orderline.order_id.planned_receiving_date.strftime("%Y-%m-%d %H:%M:%S"),  # 交货日期
            "is_bonded": "N",  # 保税 Y/N
            "first_bom_side": None,  # 板面生产顺序 0:不区分，1:正面->背面，2:背面->正面
            "lot_no": production.lot_producing_id.name,  # 生产批次
            "assigned_lotno": production.lot_producing_id.name or '',  # 指定生产批次
            "remark": None,  # 备注
            "spnl_qty": production.product_qty,  # SPNL数量

            "version": production.product_id.versions or '0',  # 版本
            "markupn_number": None,  # 拼版数
            "pnl_set_qty": pnl_qty / set_qty,  # 1P=?S 2PNL=1SET
            "pnl_pcs_qty": pnl_qty / pcs_qty,  # 1P=?P 16PNL=1PCS
            "set_pcs_qty": set_qty / pcs_qty,  # 1S=?P 8SET=1PCS
            "is_fast": "N",  # 是否加急
            # "is_risk": None,  # 是否风险批
            # "is_out_scope": None,  # 是否超制程
            # "is_code_boa": None,  # 是否芯板
            # "suffix_number": None,  # 叠层号
            # "suffix_count": None,  # 叠层个数
            # "length_of_panel": None,  # 板长
            # "width_of_panel": None,  # 板宽
            # "copper_thickness": None,  # 铜厚
            # "tp_version": None,  # 工艺线路版本
            # "put_count": None,  # 投产次数
            "field_of_app": production.product_id.application_product_id.name,  # 运用领域
            "tg_value": tg.value_id.name,  # TG值
            "level_count": level.value_id.name,  # 层数
            # "panff": None,  # 正负片流程
            "cust_model": orderline.product_id.product_tmpl_id.default_code,  # 客户型号
            # "wplan_mo_stat": None,  # 主计划工单状态 1:队列，2:在产，3:完工
            # "close_by": None,  # 完工人
            # "close_date": None,  # 完工时间
            "emergency_type": 1,  # 紧急程度 1:正常，2:VIP，3:VVIP
            # 工单（BS_MO）
            "moList": mo_list
        })
        return plans

    @classmethod
    def get_mo_list(cls, production, orderline, tg, level, child=False):
        sale = orderline.order_id
        mo_list = []
        # for move_raw in production.move_raw_ids:
        # bom_count = move_raw.product_id.bom_count
        # if bom_count == 0:  # 不获取原材料
        #     continue

        def top_production(production):
            # 最顶层production
            ids = production._get_sources()
            parent_ids = [rec for rec in ids]
            if not parent_ids:
                origin = production
            else:
                production = parent_ids[0]
                while parent_ids:
                    production = parent_ids.pop()
                    ids = production._get_sources()
                    parent_ids = [rec for rec in ids]
                origin = production
            return origin

        top_production = top_production(production)
        product_name = production.product_id.name
        if '-' in product_name:
            mo = product_name.split('-')
            layer_type = mo[1] if len(mo) > 1 else '0'
        else:
            layer_type = '0'
        mo_list.append({
            "mo_code": production.name,  # 工单编号
            "phase_code": "PCB",  # 工艺段编码
            "phase_name": product_name,  # 工艺段名称
            "erp_mo": production.name,  # ERP工单编码
            "mo_type": 3,  # 工单类型  1:量产订单，2:样品订单，3:测试订单，4:客诉订单，5:返工订单，6:补投订单
            "p_qty": production.product_qty,  # 计划数量
            "init_pbdate": sale.create_date.strftime("%Y-%m-%d %H:%M:%S"),  # 初始计划开始时间
            "init_pedate": sale.planned_receiving_date.strftime("%Y-%m-%d %H:%M:%S"),  # 初始计划结束时间
            "delivery_date": sale.planned_receiving_date.strftime("%Y-%m-%d %H:%M:%S"),  # 交货日期
            "is_outs": "N",  # 外协 Y/N
            "sup_code": None,  # 供应商编号
            "bom_ver": "1",  # BOM版本
            # "bom_ver_sku_id": None,  # BOM版本SKU明细代码
            "pt_code": None,  # 车间代码
            "cal_res_type": 2,  # 排产资源类型v  1:流水线，2:离散工位
            "tp_code": production.product_id.default_code,  # 工艺线路代码
            # "mo_stat": None,  # 生产状态  1:队列，2:在产，3:取消，4:完工，5:终止，6:委外，7:激活，8:锁定
            "mtrl_mp_stat": 0,  # 物料备料状态  0:未备料，1:备料中，2:净需求备料完成，3:实际需求备料完成
            "mo_mp_stat": 0,  # 工单备料状态  0:未备料，1:首套备料中，2:首套备料完成，3:备料完成
            # "mo_mp_bdate": None,  # 首套料备料开始时间
            # "mo_mp_edate": None,  # 首套料备料结束时间
            # "pm_dpart": None,  # 首套料备料用时
            "mo_omtrl_type": 0,  # 工单物料超发类型 0:参数控制，1:不允许超发，2:A材允许超发，3:BC材允许超发，4:ABC材允许超发
            "is_over": "N",  # 关单 Y/N
            "mo_grp": top_production.name,  # 工单组编码
            "is_bonded": "N",  # 保税 Y/N
            "is_prochk": "N",  # 程序校验 Y/N
            # "prochk_by": None,  # 程序校验人
            "prochk_date": sale.confirm_date.strftime("%Y-%m-%d %H:%M:%S"),  # 程序校验时间
            "bs_mtrl_sku_hd_id": production.product_id.default_code,  # SKU代码
            "lot_no": production.name,  # 生产批次
            # "assigned_lotno": None,  # 指定生产批次
            "fl_rule": 0,  # 出库规则  0:默认，3:不管控，1:FIFO，2:LIFO
            "rcv_type": 0,  # 领料模式  0:不启用领料，1:领料确认，2:领料交易
            "rcv_stat": 0,  # 领料状态  0:未领料，1:领料中，2:领料完成
            # "rcv_bdate": None,  # 领料开始时间
            # "rcv_edate": None,  # 领料完成时间
            # "include_pos": 0,  # 需要排位表 1是，0否
            "is_tf_lock": "N",  # 转产锁定 Y/N
            # "tf_lock_date": None,  # 转产锁定时间
            "is_complex_mo": "N",  # 组合工单 Y/N
            # "complex_mo_id": None,  # 组合代码
            # "vm_store_id": None,  # 虚拟库代码
            "have_paused": "N",  # 停过工 Y/N
            "is_mtrlover": "N",  # 物料出来完毕 Y/N
            # "moId_src": None,  # 分拆来源工单代码
            # "init_lb_type_id": None,  # 初始标签类型  待定
            # "lb_stdqty": None,  # 标签标准容量
            # "lb_rule_id": None,  # 生产标签编码规则
            # "label_temp_id": None,  # 打印模板编码
            # "olbrule_id": None,  # 外部标签编码规则代码
            "pspl_fst_type": 0,  # 首件检验维度  0:未定义，1:按工序，2:按工单，3:按班次
            # "remark": None,  # 备注
            "emergency_type": 1,  # 紧急程度 1:正常，2:VIP，3:VVIP
            # "period_code": None,  # 周期码
            # "put_count": None,  # 投产次数
            "field_of_app": production.product_id.application_product_id.name,  # 运用领域
            "tg_value": tg[0].value_id.name if tg else '0',  # TG值
            "level_count": level[0].value_id.name if level else 0,  # 层数
            # "panff": None,  # 正负片流程
            "cust_model": orderline.product_id.product_tmpl_id.default_code,  # 客户型号
            # "is_fast": None,  # 是否加急
            "mo_seq": 1,  # 批次序号
            'layer_type': layer_type,  # 层别
            'mo_class': '2' if child else '1',  # 工单类别  1:外层，2:内层，3:单层
            'hold_stat': 1,  # HOLD状态  1不管控，2管控
            'film_process': 1,  # 正/负片流程  0:正片流程 1:负片流程
            'is_out_help': "N",  # 是否委外
            'cycle_code': "",  # 周期码
            'is_rework': "Y",  # 是否返工 Y/N
            'is_group': "Y",  # 是否批次组 Y/N
            'suit_code': top_production.name,  # 套拆编码
            'lot_type': 3,  # 批次类型  1：PNL、2：SET、3：PCS
            'control_type': 1,  # 管控类型  1：批次 2：标签。3: 数量
        })
        return mo_list

    def get_unit_number(self, product_id):
        uom_panel_width = self.env.ref('pcbmanufact.uom_panel_width')
        uom_panel_length = self.env.ref('pcbmanufact.uom_panel_length')
        uom_set_width = self.env.ref('pcbmanufact.uom_set_width')
        uom_set_length = self.env.ref('pcbmanufact.uom_set_length')
        uom_pcs_width = self.env.ref('pcbmanufact.uom_pcs_width')
        uom_pcs_length = self.env.ref('pcbmanufact.uom_pcs_length')
        PCS = self.env.ref('pcbmanufact.uom_part')
        PANEL = self.env.ref('pcbmanufact.uom_panel')
        SET = self.env.ref('pcbmanufact.uom_set')
        uom_ids = product_id.uom_value_ids.mapped('uom_id')
        pnl_width = 0
        pnl_length = 0
        if uom_panel_width in uom_ids and uom_panel_length in uom_ids:
            pnl_width = product_id.uom_value_ids.filtered(lambda x: x.uom_id.id == uom_panel_width.id).uom_value
            pnl_length = product_id.uom_value_ids.filtered(lambda x: x.uom_id.id == uom_panel_length.id).uom_value
        set_width = 0
        set_length = 0
        if uom_set_width in uom_ids and uom_set_length in uom_ids:
            set_width = product_id.uom_value_ids.filtered(lambda x: x.uom_id.id == uom_set_width.id).uom_value
            set_length = product_id.uom_value_ids.filtered(lambda x: x.uom_id.id == uom_set_length.id).uom_value
        pcs_width = 0
        pcs_length = 0
        if uom_pcs_width in uom_ids and uom_pcs_length in uom_ids:
            pcs_width = product_id.uom_value_ids.filtered(lambda x: x.uom_id.id == uom_pcs_width.id).uom_value
            pcs_length = product_id.uom_value_ids.filtered(lambda x: x.uom_id.id == uom_pcs_length.id).uom_value

        pcs_qty = 1
        if PCS in uom_ids:
            pcs_qty = product_id.uom_value_ids.filtered(lambda x: x.uom_id.id == PCS.id).uom_value
        panel_qty = 1
        if PANEL in uom_ids:
            pcs_qty = product_id.uom_value_ids.filtered(lambda x: x.uom_id.id == PANEL.id).uom_value
        set_qty = 1
        if SET in uom_ids:
            set_qty = product_id.uom_value_ids.filtered(lambda x: x.uom_id.id == SET.id).uom_value
        return [pnl_width, pnl_length, set_width, set_length, pcs_width, pcs_length, panel_qty, set_qty, pcs_qty]

    def s_set_production_meslock(self, production):
        children_ids = [production]  # 全部内层MO
        visited = [production]
        production = visited[0]
        while visited and visited.pop(0):
            children_ids.extend([p for p in production._get_children()])
            visited.extend([p for p in production._get_children()])
            if visited:
                production = visited[0]
        for c in children_ids:
            c.s_mes_lock = True
        return children_ids

    def picking_send_mes(self):
        """研发单领料单同步的mes"""
        def GetChildren(production):
            children_ids = [production]  # 全部内层MO
            visited = [production]
            production = visited[0]
            while visited and visited.pop(0):
                children_ids.extend([p for p in production._get_children()])
                visited.extend([p for p in production._get_children()])
                if visited:
                    production = visited[0]
            return children_ids
        productions = self.line_ids.mapped('mrp_production_ids')
        production_list = []
        for production in productions:
            production_list.extend(GetChildren(production))
        for production in production_list:
            productions |= production
        if not productions: return
        # 板材发料
        picking_ids = productions.mapped('picking_ids').filtered(lambda x: x.picking_type_id.barcode == 'WTT-CB')
        for picking in picking_ids:
            mtrl_list = []
            base_mo = picking.origin[0:6] if picking.origin else ''
            if len(productions) == 1:
                # 无内层
                productions = productions.filtered(lambda x: x.base_mo == base_mo)
            else:
                # 有内层直接判断
                productions = productions.filtered(lambda x: x.product_id.categ_id.code == 'BCP' and x.base_mo == base_mo)
            for move in picking.move_ids_without_package:
                mtrl_list.append({
                    "wp_code": "CUT01",  # 工序代码
                    "mtrl_code": move.product_id.default_code,  # 物料编码
                    "batch_no": ','.join(move.lot_ids.mapped('name')),  # 物料批次号
                    "qty": move.product_uom_qty,  # 发料数量
                })
            data_list = []
            for p in productions:
                data_list.append({
                    "org_code": "0001",  # 组织编码
                    "mo_code": p.name,  # 工单编号, productions可能会有多个，目前只取最后一个，可能会出错
                    # 领料明细
                    "mtrl_list": mtrl_list
                })
            data = {
                "docType": "API_MO_STAT",  # 接口名称
                "updateType": "UPDATE",  # 更新类型
                "data": data_list
            }
            self.env['s.mes.upload'].upload_data(data)
